/*
 * CSeqInBits.cpp
 *
 *  Created on: Mar 9, 2010
 *      Author: yanghoch
 */

#include "SeqInBits.h"

CSeqInBits::CSeqInBits(unsigned int uiSeqLength)
{
    this->initialization(uiSeqLength);
}

CSeqInBits::CSeqInBits(const CSeqInBits& s) //Copy constructor
{
    this->initialization(s.uiSeqLength);
    unsigned int blockNo = (this->uiSeqLength % wordSize == 0 ) ? (this->uiSeqLength/wordSize) : (this->uiSeqLength/wordSize + 1);
    unsigned int  i;
    for (i = 0; i < blockNo; i ++) {
        // (replace with memcpy
        this->pUpperBits[i] = s.pUpperBits[i];
        this->pLowerBits[i] = s.pLowerBits[i];
    }
    memcpy(this->pNBits->bflag, s.pNBits->bflag, sizeof(unsigned char) * size2sizeInByte(s.pNBits->size));
}

CSeqInBits::~CSeqInBits(void)
{
    delete [] this->pLowerBits;
    delete [] this->pUpperBits;
    delete this->pNBits;
    // don't delete this->pGenome.
}

int CSeqInBits::initialization(unsigned int uiSeqLength)
{
    this->pNBits = NULL;
    this->pLowerBits = NULL;
    this->pUpperBits = NULL;
    return (allocBitStrSpace(uiSeqLength));
}

int CSeqInBits::allocBitStrSpace(unsigned int uiSeqLength)
{
    if (uiSeqLength > 0) {
        if (this->pLowerBits == NULL && this->pUpperBits == NULL && this->pNBits == NULL) {
            this->uiSeqLength = uiSeqLength;
            unsigned int uiSeqLengthInWordSize = lengthInWordSize(uiSeqLength); // add one more
            this->pLowerBits = new WORD_SIZE[uiSeqLengthInWordSize];
            this->pUpperBits = new WORD_SIZE[uiSeqLengthInWordSize];
            this->pNBits = new CboolFlagArray(uiSeqLength);
            memset(pLowerBits, 0x00, sizeof(WORD_SIZE) * uiSeqLengthInWordSize);
            memset(pUpperBits, 0x00, sizeof(WORD_SIZE) * uiSeqLengthInWordSize);
        } else {
            ERR;
        }
    }
    return(0);
}

// TODO test this function
void CSeqInBits::decode(char* caRead, int bufferSize)
{
    this->decodeSubStr(0, this->uiSeqLength, caRead, wordSize * (this->uiSeqLength/wordSize + 2) + 1);
}

// return a const char pointer point to the start of buffer as the decoded string
// end should be the position that after the last position.
// Test case (0, 0) (0, 64), (0, 92), (64, 92)
char* CSeqInBits::decodeSubStr(unsigned int start, unsigned int end, char* caBuf, unsigned int BufferMAX)
{
    unsigned int subStrLength = (end > start) ? (end - start) : 0;
    unsigned int startBlock = start/wordSize;
    unsigned int endBlock = (start + subStrLength) / wordSize;
    unsigned int blockNo = subStrLength/wordSize + 2; //ex: (32 95) needs two blocks
    if (start % wordSize == 0) {
        blockNo--;
    }
    if (end % wordSize == 0) {
        blockNo--;
    }
    if (start > end) {
        printf("Start index %u is greater than the end index %u\n", start, end);
    } else if ((blockNo * wordSize + 1) >= BufferMAX) {
        printf("Decode substring has length %u, longer than the buffer size %u\n", blockNo * wordSize, BufferMAX);
    } else if (end > this->uiSeqLength) {
        printf("Decode substring end %u is greater than the seq length %u\n", end, this->uiSeqLength);
    } else {
        unsigned int  i;
        for (i = startBlock; i <= endBlock; i ++) {
            long long unsigned upperBlock = this->pUpperBits[i];
            long long unsigned lowerBlock = this->pLowerBits[i];
            decodeRead(&caBuf[(i- startBlock) * wordSize], (int)wordSize,  upperBlock, lowerBlock);
        }
        char* pSubStr = &caBuf[start%wordSize];
        pSubStr[end-start] = '\0';
        return(pSubStr);
        // TODO make sure the first and last block is correct
    }
    caBuf[0] = '\0';
    return(caBuf);
}

/* Note the chromosome encoding is NOT continuous. The more significant bits of each words encodes
 * nucleotides in front of those less significant bits. This encodeing make bits incontinuously mapped
 * to chromosome index. Note the first bit of each WORD represent the first base of each section
 */
CReadInBits CSeqInBits::getSubstringInBits(unsigned int uiSeqPosition) const
{
    CReadInBits r;
    unsigned int uiSeqLengthInWordSize = lengthInWordSize(this->uiSeqLength);
    unsigned int indexInWords = uiSeqPosition / wordSize;
    unsigned int bitsShift = uiSeqPosition % wordSize;
    if (uiSeqLengthInWordSize > indexInWords) {
        r.UpperBits = this->pUpperBits[indexInWords] >> bitsShift;
        r.LowerBits = this->pLowerBits[indexInWords] >> bitsShift;
        if (bitsShift != 0) {
            r.UpperBits |= (this->pUpperBits[indexInWords + 1] << (wordSize - bitsShift));
            r.LowerBits |= (this->pLowerBits[indexInWords + 1] << (wordSize - bitsShift));
        }
    } else
        cout << "Warning, wrong genome index " << endl;
    return(r);
}

// eliminate the tail bits out of read length range
CReadInBits CSeqInBits::getSubstringInBits\
(unsigned int uiPosIndex, unsigned int uiSubstringLength) const
{
    CReadInBits r;
    unsigned int indexInWords = uiPosIndex / wordSize;
    unsigned int bitsShift = uiPosIndex % wordSize;
    r.UpperBits = this->pUpperBits[indexInWords] >> bitsShift;
    r.LowerBits = this->pLowerBits[indexInWords] >> bitsShift;
    if (bitsShift != 0) {
        r.UpperBits |= (this->pUpperBits[indexInWords + 1] << (wordSize - bitsShift));
        r.LowerBits |= (this->pLowerBits[indexInWords + 1] << (wordSize - bitsShift));
    }
    unsigned int elimatedBitsNo = wordSize - uiSubstringLength;
    r.UpperBits <<= elimatedBitsNo;
    r.LowerBits <<= elimatedBitsNo;
    r.UpperBits >>= elimatedBitsNo;
    r.LowerBits >>= elimatedBitsNo;
    return (r);
}
